﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace LuaVM.Core
{
	/// <summary>
	/// Lua插件类
	/// </summary>
	public abstract class LuaPlugin
	{
		/// <summary>
		/// 插件名称
		/// </summary>
		public abstract string Name { get; }

		/// <summary>
		/// 插件在Lua环境中的键值
		/// </summary>
		public string PluginKey => "plugins." + Name;

		/// <summary>
		/// 当前Lua环境
		/// </summary>
		public LuaEnv Lua { get; private set; }

		/// <summary>
		/// 安装时自动创建的Lua环境插件对象
		/// </summary>
		public LuaTable Plugin { get; private set; }

		/// <summary>
		/// 插件是否已安装
		/// </summary>
		public bool Attached => Lua != null;

		/// <summary>
		/// 插件导出的所有变量名称
		/// </summary>
		public virtual string[] ExportedVariables { get; }

		/// <summary>
		/// 插件导出的所有函数名称
		/// </summary>
		public virtual string[] ExportedFunctions { get; }
		
		const string TempTable = "__temp_table";

		/// <summary>
		/// 在插件环境中创建一个Lua表对象
		/// </summary>
		/// <param name="path">储存路径，如果为null则使用临时路径（创建的对象无法通过GetTable获取）</param>
		/// <returns>创建的LuaTable对象，创建失败则返回null</returns>
		public LuaTable NewTable(string path = null)
		{
			if (string.IsNullOrEmpty(path))
			{
				path = TempTable;
			}

			return Lua.NewTable(PluginKey + '.' + path);
		}

		/// <summary>
		/// 获取插件环境中某个Lua表
		/// </summary>
		/// <param name="path">路径</param>
		/// <returns>LuaTable对象，如果不存在则返回null</returns>
		public LuaTable GetTable(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				path = TempTable;
			}

			return Lua.GetTable(PluginKey + '.' + path);
		}

		/// <summary>
		/// 将一个变量值注册入插件环境
		/// </summary>
		/// <param name="name">变量在插件环境中的名称</param>
		/// <param name="value">变量值</param>
		public void RegisterVariable(string name, object value)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentException("RegisterVariable: name is required.");
			}

			Lua[PluginKey + '.' + name] = value;
		}

		/// <summary>
		/// 将一个静态函数注册入插件环境
		/// </summary>
		/// <typeparam name="T">静态函数所属的class</typeparam>
		/// <param name="name">静态函数名称</param>
		public void RegisterFunction<T>(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentException("RegisterFunction: name is required.");
			}

			Lua.RegisterFunction<T>(name, PluginKey + '.' + name);
		}

		/// <summary>
		/// 将一个类成员函数（方法）注册入插件环境
		/// </summary>
		/// <typeparam name="T">成员函数所属的class</typeparam>
		/// <param name="instance">类实例</param>
		/// <param name="name">成员函数名，必须是定义于委托类中的public成员</param>
		public void RegisterFunction<T>(T instance, string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentException("RegisterFunction: name is required.");
			}

			Lua.RegisterFunction(instance, name, PluginKey + '.' + name);
		}

		/// <summary>
		/// 安装到Lua环境
		/// </summary>
		/// <param name="lua">Lua环境</param>
		public void Attach(LuaEnv lua)
		{
			if (string.IsNullOrEmpty(Name) || !Regex.IsMatch(Name, "^[a-zA-Z_][0-9a-zA-Z_]*$"))
			{
				throw new PluginNameException();
			}

			Lua = lua;
			Plugin = lua.NewTable(PluginKey);			
			OnAttach();
		}

		/// <summary>
		/// 从Lua环境卸载
		/// </summary>
		public void Detach()
		{
			OnDetach();
			Plugin = null;
			Lua = null;
		}		
	
		/// <summary>
		/// 安装时调用
		/// </summary>
		protected virtual void OnAttach()
		{
		}

		/// <summary>
		/// 卸载时调用
		/// </summary>
		protected virtual void OnDetach()
		{
		}

		private static string GetItemName(string text)
		{
			if (string.IsNullOrEmpty(text) || text.EndsWith("."))
			{
				return null;
			}			

			int index = text.LastIndexOf('.');
			if (index == -1)
			{
				return text;
			}

			return text.Substring(index + 1);
		}
	}

	/// <summary>
	/// 当Name属性不合法的插件尝试安装时抛出
	/// </summary>
	public class PluginNameException : Exception
	{
		/// <summary>
		/// 异常消息
		/// </summary>
		public override string Message => "Plugin names must match regex \"^[a-zA-Z_][0-9a-zA-Z_]*$\"";
	}
}
